/*
 * shape_context.cpp -- Shape context demo for shape matching
 */
#include <iostream>
#include <opencv2/core/utility.hpp>
#include <string>
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/shape.hpp"
using namespace std;
using namespace cv;

static void help()
{
    printf("\n"
           "This program demonstrates a method for shape comparison based on Shape Context\n"
           "You should run the program providing a number between 1 and 20 for selecting an image in the folder "
           "../data/shape_sample.\n"
           "Call\n"
           "./shape_example [number between 1 and 20, 1 default]\n\n");
}

static vector<Point> simpleContour(const Mat& currentQuery, int n = 300)
{
    vector<vector<Point>> _contoursQuery;
    vector<Point>         contoursQuery;
    findContours(currentQuery, _contoursQuery, RETR_LIST, CHAIN_APPROX_NONE);
    for (auto& border : _contoursQuery) {
        for (auto p : border) {
            contoursQuery.push_back(p);
        }
    }
    // In case actual number of points is less than n
    int dummy = 0;
    for (auto add = contoursQuery.size() - 1; add < n; add++) {
        contoursQuery.push_back(contoursQuery[dummy++]); // adding dummy values
    }
    // Uniformly sampling
    cv::randShuffle(contoursQuery);
    vector<Point> cont;
    for (int i = 0; i < n; i++) {
        cont.push_back(contoursQuery[i]);
    }
    return cont;
}

int main(int argc, char** argv)
{
    string                path = "../shape_sample/";
    cv::CommandLineParser parser(argc, argv, "{help h||}{@input|1|}");
    if (parser.has("help")) {
        help();
        return 0;
    }
    int indexQuery = parser.get<int>("@input");
    if (!parser.check()) {
        parser.printErrors();
        help();
        return 1;
    }
    if (indexQuery < 1 || indexQuery > 20) {
        help();
        return 1;
    }
    cv::Ptr<cv::ShapeContextDistanceExtractor> mysc = cv::createShapeContextDistanceExtractor();
    Size                                       sz2Sh(300, 300);
    stringstream                               queryName;
    queryName << path << indexQuery << ".png";
    auto query = imread(queryName.str(), IMREAD_GRAYSCALE);
    Mat  queryToShow;
    resize(query, queryToShow, sz2Sh, 0, 0, INTER_LINEAR_EXACT);
    imshow("QUERY", queryToShow);
    moveWindow("TEST", 0, 0);
    vector<Point> contQuery = simpleContour(query);
    int           bestMatch = 0;
    float         bestDis   = FLT_MAX;
    for (int ii = 1; ii <= 20; ii++) {
        if (ii == indexQuery) continue;
        waitKey(30);
        stringstream iiname;
        iiname << path << ii << ".png";
        cout << "name: " << iiname.str() << endl;

        auto iiIm = imread(iiname.str(), 0);
        Mat  iiToShow;
        resize(iiIm, iiToShow, sz2Sh, 0, 0, INTER_LINEAR_EXACT);
        imshow("TEST", iiToShow);
        moveWindow("TEST", sz2Sh.width + 50, 0);

        auto contii = simpleContour(iiIm);
        auto dis    = mysc->computeDistance(contQuery, contii);
        if (dis < bestDis) {
            bestDis   = dis;
            bestMatch = ii;
        }
        std::cout << " distance between " << queryName.str() << " and " << iiname.str() << " is: " << dis << std::endl;
    }
    destroyWindow("TEST");
    stringstream bestname;
    bestname << path << bestMatch << ".png";
    Mat iiIm = imread(bestname.str(), 0);
    Mat bestToShow;
    resize(iiIm, bestToShow, sz2Sh, 0, 0, INTER_LINEAR_EXACT);
    imshow("BEST MATCH", bestToShow);
    moveWindow("BEST MATCH", sz2Sh.width + 50, 0);
    waitKey();
    return 0;
}
